#pragma once

#include "headers.h"

namespace Joe {

class code {
private:
    std::vector<std::string> lines;
    std::vector<std::string> words;
    const std::string operator_characters = R"(\+\-\*/=><!\(\)\[\]\{\}\:&\|;\,\.~\^)"; // 所有运算符字符
    std::regex word_regex;
    std::string compile_errors;

    bool is_identifier_character(char character) {
        return character == '_' || (character >= 'a' && character <= 'z')
            || (character >= 'A' && character <= 'Z')
            || (character >= '0' && character <= '9');
    }

    bool is_operator_character(char character) {
        for (const char& operator_character : this->operator_characters) {
            if (operator_character != '\\' && character == operator_character) return true;
        }
        return false;
    }

    void init() {
        word_regex = std::regex(
            R"(("(?:\\"|[^"])*(?:"|$)))" // 字符串（判断结尾是不是"）
            R"(|('(?:\\'|[^'])*(?:'|$)))" // 字符（判断结尾是不是'）
            R"(|(//.*$))" // 注释
            R"(|(^\d+(?:\.\d+)?(?:$|\W)))" // 数（字符串开头）（去除结尾）
            R"(|(^\w+(?:$|\W)))" // 标识符（字符串开头）（去除结尾）（判断开头是不是数字）
            R"(|(^[)" + operator_characters + R"(](?:$|[^)"
                + operator_characters + R"(])))" // 运算符字符（字符串开头）（去除结尾）
            R"(|((?:^|\W)\d+(?:\.\d+)?(?:$|\W)))" // 数（去除开头结尾）
            R"(|((?:^|\W)\w+(?:$|\W)))" // 标识符（去除开头结尾）（判断开头是不是数字）
            R"(|((?:^|[^)" + operator_characters + R"(])[)"
                + operator_characters + R"(](?:$|[^)"
                + operator_characters + R"(])))" // 运算符字符（去除开头结尾）
        );
    }

    void compile_init() {
        this->words.clear();
        this->compile_errors = "";
    }

    void add_compile_error(int line_n, int line_index, std::string error_content) {
        this->compile_errors += "第" + std::to_string(line_n + 1) + "行，第"
            + std::to_string(line_index + 1) + "个字符："
            + error_content + "\n";
    }

    void split() {
        int line_index, word_position;
        std::smatch word_match;
        std::string sub_line;
        std::string word;
        bool is_error;
        for (int line_n = 0; line_n < this->lines.size(); ++line_n) {
            const std::string& line = this->lines[line_n];
            line_index = 0;
            while (line_index < line.size()) {
                sub_line = line.substr(line_index);
                if (std::regex_search(sub_line, word_match, this->word_regex)) {
                    for (int word_match_group_n = 1; word_match_group_n < word_match.size(); ++word_match_group_n) {
                        if (word_match[word_match_group_n].matched) {
                            is_error = false;
                            word = word_match.str();
                            word_position = word_match.position();
                            switch (word_match_group_n) {
                            case 1:
                                if (word.size() == 1 || word.back() != '\"') {
                                    this->add_compile_error(line_n, word_position + line_index, "字符串未闭合");
                                    is_error = true;
                                    break;
                                }
                                break;
                            
                            case 2:
                                if (word.size() == 1 || word.back() != '\'') {
                                    this->add_compile_error(line_n, word_position + line_index, "字符未闭合");
                                    is_error = true;
                                    break;
                                }
                                break;
                            

                            case 4:
                                if (!this->is_identifier_character(word.back())) {
                                    word = word.substr(0, word.size() - 1);
                                }
                                break;
                            
                            case 5:
                                if (word.front() >= '0' && word.front() <= '9') {
                                    this->add_compile_error(line_n, word_position + line_index, "标识符不能以数字开头");
                                    is_error = true;
                                    break;
                                }
                                if (!this->is_identifier_character(word.back())) {
                                    word = word.substr(0, word.size() - 1);
                                }
                                break;
                            
                            case 6:
                                if (!this->is_operator_character(word.back())) {
                                    word = word.substr(0, word.size() - 1);
                                }
                                break;
                            

                            case 7:
                                if (!this->is_identifier_character(word.front())) {
                                    word = word.substr(1);
                                    ++word_position;
                                }
                                if (!this->is_identifier_character(word.back())) {
                                    word = word.substr(0, word.size() - 1);
                                }
                                break;

                            case 8:
                                if (!this->is_identifier_character(word.front())) {
                                    word = word.substr(1);
                                    ++word_position;
                                }
                                if (word.front() >= '0' && word.front() <= '9') {
                                    this->add_compile_error(line_n, word_position + line_index, "标识符不能以数字开头");
                                    is_error = true;
                                    break;
                                }
                                if (!this->is_identifier_character(word.back())) {
                                    word = word.substr(0, word.size() - 1);
                                }
                                break;
                            
                            case 9:
                                if (!this->is_operator_character(word.front())) {
                                    word = word.substr(1);
                                    ++word_position;
                                }
                                if (!this->is_operator_character(word.back())) {
                                    word = word.substr(0, word.size() - 1);
                                }
                                break;
                            }
                            if (!is_error) {
                                this->words.push_back(word);
                            }
                            line_index += word_position + word.size();
                            break;
                        }
                    }
                } else {
                    break;
                }
            }
        }
    }

public:
    code() {
        this->init();
    }

    code(std::string file_path) {
        this->init();
        this->read_file(file_path);
    }

    std::vector<std::string> get_lines() const {
        return this->lines;
    }

    std::vector<std::string> get_words() const {
        return this->words;
    }

    bool read_file(std::string file_path) {
        std::ifstream file_stream;
        file_stream.open(file_path);
        if (!file_stream.is_open()) {
            return false;
        }
        this->lines.clear();
        std::string line;
        while (std::getline(file_stream, line)) {
            this->lines.push_back(line);
        }
        file_stream.close();
        return true;
    }

    void print_code() const {
        for (const std::string& line : this->lines) {
            std::cout << line << "\n";
        }
    }
    
    bool compile() {
        this->compile_init();
        this->split();

        for (const auto& word : words) {
            std::cout << word << "\n";
        }

        if (this->compile_errors == "") {
            return true;
        } else {
            std::cout << "编译错误:\n" + this->compile_errors;
            return false;
        }
    }
};

}